import os
import fmt
import parker.cgroup
import libc_temp
import path
import time
import syscall
import parker.log as *

string charset = 'abcdefghijklmnopqrstuvwxyz0123456789'

fn arg_version():bool {
    var args = os.args()

    // 没有参数
    if args.len == 1 {
        return true
    }

    if args.len == 2 && (args[1] == '-v' || args[1] == '--version') {
        return true
    }

    return false
}

fn arg_verbose():bool {
    var flag = syscall.get_env('PARKER_VERBOSE')
    return flag != ''
}

// 从 exe_path 中提取完整的数据
fn extract_tgz(string exe_path):[u8] {
    var fd = syscall.open(exe_path, syscall.O_RDONLY, 0666)

    syscall.seek(fd, -16, syscall.SEEK_END)

    var size_buf = vec<u8>{len=16}
    var len = syscall.read(fd, size_buf)
    if len != size_buf.len {
        syscall.close(fd)
        throw fmt.sprintf('read fd len %d exception', len)
    }

    var size_str = size_buf as string

    // atoi
    var size = strtol(size_str.ref(), 0, 10)
    if size == 0 {
        throw 'extract tgz size is zero'
    }

    logf('read tgz tail 16 size=%d success', size)

    syscall.seek(fd, -16 - size, syscall.SEEK_END)

    // read result
    var result = vec<u8>{len=size}

    len = syscall.read(fd, result)
    if len != size {
        throw fmt.sprintf('read fd result len %d != size %d', len, size)
    }

    // close fd
    syscall.close(fd)

    return result
}

// 在 workdir 创建 mount 空间
fn mount_ns(string workdir) {
    syscall.unshare(syscall.CLONE_NEWNS)

    // mount / 
    syscall.mount('none', '/', '', syscall.MS_REC|syscall.MS_PRIVATE, "")

    // mount("tmpfs", workdir, "tmpfs", 0, NULL)
    syscall.mount('tmpfs', workdir, 'tmpfs', 0, '')
}
    
fn read_target(string workdir):string {
    var target_path = path.join(workdir, '.target_name')
    var fd = syscall.open(target_path, syscall.O_RDONLY, 0)

    var target_buf = vec<u8>{len=1024}
    var len = syscall.read(fd, target_buf)
    target_buf = target_buf.slice(0, len)
    
    var target_name = target_buf as string
    
    return path.join(workdir, target_name)
}

fn run_target(cgroup.cgroup_t cg, string target_path):int {
    // - fork 默认继承父进程的 mount ns
    // - fork, 父进程直接返回 pid 即可
    var pid = syscall.fork()
    if pid > 0 {
        // parent
        return pid
    }

    // pdeathsig
    // 1 = PR_SET_PDEATHSIG
    // 9 = SIGKILL
    var result = prctl(1, 9, 0, 0, 0)
    if result == -1 {
       throw fmt.sprintf('set pdeathsig err=%s', libc_strerror())
    }

    // 读取当前进程 id
    pid = syscall.getpid()
    logf('fork success, current is child, pid is %d', pid)

    // 将 pid 注册到 cgroup 中
    cg.register(pid)
    
    // run target with all args and env
    var args = os.args()
    // args[0] 是 proc/:pid/comm, 这里替换成 target name
    var target_name = path.base(target_path)
    args[0] = target_name

    // 通过 exec 进行进程启动
    syscall.exec(target_path, args, syscall.get_envs())
    logf('exec cannot execute it here')
    return 0 // 正常不会执行到这里了
}

// TODO 多线程模式中无法实现
// 创建一个能够拦截所有信号且非阻塞的 fd
fn sig_notify():int {
    var mask_ptr = new sigset_t // ptr<sigset_t>

    // 注册所有信号
    if sigfillset(mask_ptr)  == -1 {
        throw libc_strerror()
    }

    // 0 = SIG_BLOCK
    if (sigprocmask(0, mask_ptr, null) == -1) {
        throw libc_strerror()
    }

    var sigfd = signalfd(-1, mask_ptr, 2048)
    if sigfd == -1 {
        throw libc_strerror()
    }
    
    return sigfd as int
}

fn rand_letter(int len):string {
    var result = vec<u8>{len=len}
    srand(time.now().timestamp() as u32)

    for i,v in result {
        int char_index = rand() % charset.len
        result[i] = charset[char_index]
    }
    
    return result as string
}
